/*
 * Copyright 2006 - 2007 - Chris Maki
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.theserverside.blog.model;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;

import javax.persistence.AssociationOverride;
import javax.persistence.AssociationOverrides;
import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.Lob;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;
import javax.persistence.Table;

@Entity
@Table(name = "COMMENT")
@AttributeOverrides( { @AttributeOverride(name = "id", column = @Column(name = "COMMENT_ID")),
    @AttributeOverride(name="version", column=@Column(name="COMMENT_VERSION")),
    @AttributeOverride(name="dateCreated", column=@Column(name="COMMENT_DATE_CREATED")),
    @AttributeOverride(name="dateUpdated", column=@Column(name="COMMENT_DATE_UPDATED"))
    })
@AssociationOverrides( {
    @AssociationOverride(name="createdByUser", joinColumns=@JoinColumn(name="COMMENT_CREATED_BY_USER_ID")),
    @AssociationOverride(name="updatedByUser", joinColumns=@JoinColumn(name="COMMENT_UPDATED_BY_USER_ID"))
})
@NamedQueries({
    // select comments that do not have parent comments
    @NamedQuery(name = "comment.rootComments", query = "SELECT c FROM Comment c WHERE c.parent IS NULL"),
    // select comments made by a User
    @NamedQuery(name = "comment.userComments", query = "SELECT c FROM Comment c WHERE c.createdByUser = :user"),
    // count the number of comments made by a user
    @NamedQuery(name = "comment.userCount", query = "SELECT COUNT(c) FROM Comment c WHERE c.createdByUser = :user"),
    // select the comments a user made without responses
    @NamedQuery(name = "comment.noChildren", 
            query = "SELECT c FROM Comment c WHERE c.children IS EMPTY AND c.parent IS NULL and c.createdByUser = ?1")
})
public class Comment extends ModelBase implements Serializable{

    private static final long serialVersionUID = 6754723278919588319L;

    @Column(name="COMMENT_UUID")
    private String uuid = UUID.randomUUID().toString();
    
    @Column(name = "COMMENT_TITLE")
    private String title;

    @Lob
    @Column(name="COMMENT_COMMENT")
    private String content;

    @Column(name="COMMENT_EDIT_COUNT")
    private Integer editCount;
    
    @ManyToOne
    @JoinColumn(name="COMMENT_PARENT")
    private Comment parent;

    @OneToMany(mappedBy="parent", cascade={CascadeType.MERGE, CascadeType.PERSIST})
    @OrderBy("dateCreated DESC")
    private List<Comment> children = new ArrayList<Comment>();
    
    @ManyToOne
    @JoinColumn(name="COMMENT_BLOG_POST_ID")
    private BlogPost blogPost;

    public Comment() {
        super();
    }
    public Comment(String title, String content) {
        this.title = title;
        this.content = content;
    }
    
    public String getUuid() {
        return uuid;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public String getContent() {
        return content;
    }
    public void setContent(String content) {
        this.content = content;
    }
    public Integer getEditCount() {
        return editCount;
    }
    public void setEditCount(Integer editCount) {
        this.editCount = editCount;
    }
    public BlogPost getBlogPost() {
        return blogPost;
    }
    /**
     * Since the BlogPost to Comment relationship is managed by the BlogPost,
     * you cannot set the blog post this instance is connected to. To move a
     * comment from one blog post to another, just add the comment to a new post
     * and it will be disconnected from the other post and added to this
     * instance
     * 
     * @param blogPost
     *            the blog post this comment belongs to
     */
    void setBlogPost(BlogPost blogPost) {
        this.blogPost = blogPost;
    }
    
    public Comment getParent() {
        return parent;
    }

    /**
     * Only the addChildPost method can set the parent of a post.
     * @param parent the parent of this post
     * @see #addChildComment(BlogPost)
     */
    private void setParent(Comment parent) {
        this.parent = parent;
    }
    
    /**
     * Returns an immutable list of Children. To add or remove children use the
     * <code>addChild()</code> or <code>removeChild()</code>
     * 
     * @return immutable list of Children
     */
    public List<Comment> getChildren() {
        return Collections.unmodifiableList(children);
    }

    /**
     * Makes <code>comment</code> a child of this instance. Throws IllegalArgumentException if
     * <code>comment</code> is null.
     * 
     * @param comment child comment
     */
    public void addChildComment(Comment comment) {
        if (comment == null) throw new IllegalArgumentException("child comment is null!");

        // check to see if comment has a parent
        if (comment.getParent() != null) {
            // if there is a parent check to see if it's already associated to this comment
            if (comment.getParent().equals(this)) {
                // if this instance is already the parent, we can just return
                return;
            } 
            else {
                // disconnect post from it's current relationship
                comment.getParent().children.remove(this);
            }
        }
        
        // make this instance the new parent
        comment.setParent(this);
        children.add(comment);
    }
    
    /**
     * Disconnects <code>comment</code> from this instance.
     * 
     * @param comment
     *            child comment
     */
    public void removeChildComment(Comment comment) {
        if (comment == null) throw new IllegalArgumentException("child comment is null!");

        // make sure we are the parent before we break the relationship
        if (comment.parent != null && comment.getParent().equals(this)) {
            comment.setParent(null);
            children.remove(comment);
        }
        else {
            throw new IllegalArgumentException("child comment not associated with this instance");
        }
    }

    @Override
    public int hashCode() {
       return uuid.hashCode();
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null) return false;
        if (getClass() != obj.getClass()) return false;
        final Comment other = (Comment) obj;
        if (uuid == null) {
            if (other.getUuid() != null) return false;
        }
        else if (!uuid.equals(other.getUuid())) return false;
        return true;
    }
    @Override
    public String toString() {
        return String.format("Title: %s, Comment: %s, UUID: %s", title, content, uuid);
    }
}
